package com.ainiyar.springweb.config.security.filter;

import com.ainiyar.springweb.common.exception.CustomExceptionAdvice;
import com.ainiyar.springweb.common.exception.CustomerAuthenticationException;
import com.ainiyar.springweb.config.security.handler.LoginFailureHandler;
import com.ainiyar.springweb.config.security.service.CustomerAdminDetailsService;
import com.ainiyar.springweb.util.JWTUtil;
import com.ainiyar.springweb.util.RedisUtil;
import com.ainiyar.springweb.util.TokenUtil;
import com.ainiyar.springweb.util.ToolsUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;

/**************************************
 * File Name : AuthCheckTokenFilter
 * Created with : IntelliJ IDEA.
 * Author : 艾尼亚尔·毛拉吾提
 * QQ : 524155792  WeChat : ainiyar0124
 * Created Time : 2024/2/4  18:14
 * Desc : 请求验证器
 ***************************************/
@Component
public class AuthCheckFilter extends OncePerRequestFilter {
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private LoginFailureHandler loginFailureHandler;
    @Resource
    private JWTUtil jwtUtil;
    @Resource
    private CustomerAdminDetailsService adminDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            //获取当前请求的url
            String url = request.getRequestURI();
            String[] urls = {"/admin/do_login", "/admin/captcha","/admin/get_config", "/admin/file/**", "/applet/**"};
            //如果后台登录则进行验证验证码
            if (url.equals("/admin/do_login")) {
                //判断验证码参数是否存在
                if (ToolsUtil.isParamAbsentOrEmpty(request, "captcha_key") || ToolsUtil.isParamAbsentOrEmpty(request, "captcha_code")) {
                    throw new RuntimeException("请输入验证码、验证码不能为空！");
                }
                //验证码验证
                String captcha_key = request.getParameter("captcha_key");
                //检查当前验证码是否在redis中存在
                if (!redisUtil.hasKey(captcha_key)) {
                    throw new CustomExceptionAdvice("无效的验证码，请刷新验证码");
                }
                String captcha_value = request.getParameter("captcha_code");
                Object redis_captcha = redisUtil.get(captcha_key);
                //判断输入的验证码值和redis中的值是否相同
                if (!captcha_value.equalsIgnoreCase((String) redis_captcha)) {
                    throw new CustomExceptionAdvice("验证码错误，请重新输入验证码");
                }
                //验证码验证通过后，从redis中释放当前验证码缓存
                redisUtil.del(Collections.singletonList(captcha_key));
            }
            //判断当前请求是否是登录请求，如果不是登录请求，则需要验证token
            if (!ToolsUtil.isUrlMatched(url, urls)) {
                //进行验证token
                authenticationToken(request, response);
            }
        } catch (AuthenticationException e) {
            loginFailureHandler.onAuthenticationFailure(request, response, e);
        }
        doFilter(request, response, filterChain);
    }

    private void authenticationToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String token = TokenUtil.handelToken(request);
        //如果token为空、则抛出异常
        if (ToolsUtil.isNullOrEmpty(token)) {
            throw new CustomerAuthenticationException("token失效，请重新登录");
        }
        //token已过期
        if (jwtUtil.isTokenExpired(token)) {
            throw new CustomerAuthenticationException("token已过期，请重新登录");
        }
        //如果token存在、则token中解析出管理员名称
        String admin_name = jwtUtil.extractUserName(token);
        //判断管理员名称是否为空
        if (ToolsUtil.isNullOrEmpty(admin_name)) {
            throw new CustomerAuthenticationException("token解析失败");
        }
        //获取用户信息
        UserDetails userDetails = adminDetailsService.loadUserByUsername(admin_name);
        //判断管理员信息是否为空
        if (userDetails == null) {
            throw new CustomerAuthenticationException("token验证失败");
        }
        //创建管理员身份认证对象
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        //设置请求设置
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        //将认证的信息交给spring-security的上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}

